﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Text;

namespace nwrole.lib {
    public class PluginServiceHost {
        private Guid serviceId;
        private int Port;
        private int SupervisorPort;
        private PluginManager manager;
        private Uri baseAddress;
        private ServiceHost serviceHost;
        private string serviceName;
        private PluginProviderBase pluginProvider;

        private static void WriteLog(string content) {
            try {
                string logDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "log/PluginService/");
                if (!System.IO.Directory.Exists(logDir))
                    System.IO.Directory.CreateDirectory(logDir);

                DateTime now = DateTime.Now;

                System.IO.File.AppendAllText(logDir + "log " + now.ToString("yyyy-MM-dd") + ".txt", now.ToString("yyyy-MM-dd HH:mm:ss ") + content + "\r\n");
            } catch {
            }
        }
        
        /// <summary>
        /// Creates with a random Guid and no connection to a Supervisor
        /// </summary>
        /// <param name="port">Set to 0 by default which results in no ServiceHost being created</param>
        public PluginServiceHost(string pluginProviderClass, int port = 0) {
            serviceName = "UnnamedService";
            serviceId = Guid.NewGuid();
            Port = port;
            SupervisorPort = 0;
            pluginProvider = CreatePluginProvider(pluginProviderClass, SupervisorPort);
        }

        public PluginServiceHost(string pluginProviderClass, Guid serviceId, string serviceName, int port, int supervisorPort) {
            WriteLog(String.Format("PluginServiceHost creating with name:{0} Id:{1} port:{2}.", serviceName, serviceId, port));
            this.serviceName = serviceName;
            this.serviceId = serviceId;
            Port = port;
            SupervisorPort = supervisorPort;
            pluginProvider = CreatePluginProvider(pluginProviderClass, supervisorPort);
        }

        private PluginProviderBase CreatePluginProvider(string className, int supervisorPort) {
            //IPluginProvider instance = new TestPluginProvider();

            //Type test = Type.GetType("nwrole.Supervisor.WorkerRole.AzurePluginProvider, nwrole.Supervisor.WorkerRole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
            Type type = Type.GetType(className);
            PluginProviderBase instance = (PluginProviderBase)Activator.CreateInstance(type);
            instance.SetServiceName(serviceName);
            instance.SupervisorPort = supervisorPort;
            instance.Initialize();

            return instance;
        }

        public void Start() {
            WriteLog("PluginServiceHost starting for serviceId: " + serviceId);
            EventLogger.LogInformation("PluginServiceHost.Start", String.Format("Starting Plugin service '{0}' ({1}).", serviceId, Port));

            try {
                if (serviceHost != null)
                    serviceHost.Close();
                if (manager != null)
                    manager.Stop();

                manager = new PluginManager(pluginProvider, serviceId, serviceName, SupervisorPort);

                if (Port > 0) {
                    baseAddress = new Uri("net.tcp://localhost:" + Port.ToString());
                    serviceHost = new ServiceHost(manager, baseAddress);
                    NetTcpBinding binding = new NetTcpBinding();
                    binding.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
                    binding.MaxReceivedMessageSize = 2147483647;
                    serviceHost.AddServiceEndpoint(typeof (IServiceContract), binding, serviceId.ToString("N"));
                    serviceHost.Open();
                }

                manager.Start();
            } catch (Exception exception) {
                WriteLog("PluginServiceHost exception: " + exception);
                if (exception.InnerException != null)
                  WriteLog("PluginServiceHost inner exception: " + exception.InnerException);
                else
                  WriteLog("No inner exception");
                EventLogger.LogException("PluginServiceHost.Start", exception);
            }
            EventLogger.LogInformation("PluginServiceHost.Start", String.Format("Started Plugin service '{0}' ({1}).", serviceId, Port));
        }

        public void Stop() {
            WriteLog("PluginServiceHost stopping serviceId: " + serviceId);
            EventLogger.LogInformation("PluginServiceHost.Stop", String.Format("Stopping Plugin service '{0}' ({1}).", serviceId, Port));

            if (manager != null) {
                manager.Stop();
                manager = null;
            }

            if (serviceHost != null) {
                try {
                    serviceHost.Close();
                    serviceHost = null;
                } catch (Exception exception) {
                    EventLogger.LogException("PluginServiceHost.Stop", exception);
                }
                EventLogger.LogInformation("PluginServiceHost.Stop", String.Format("Stopped Plugin service '{0}' ({1}).", serviceId, Port));
            }
        }
    }
}
